#coding: utf-8
import httplib
import urlparse
import threading
import os, string, sys

class MyHTTP(threading.Thread):
    def __init__(self, url, counter, mid):
        threading.Thread.__init__(self)
        self.url = url
        self.h = None
        self.fid = mid
        self.counter = counter
        self.startpos = counter.get_start(mid)
        self.bsize = counter.get_size(mid)
        urlls = urlparse.urlparse(self.url)
        self.host = urlls[1]
        self.filename = os.path.basename(urlls[2])
        
    def getheader(self):        
        self.h = httplib.HTTP()
        #self.h.set_debuglevel(1)
        self.h.connect(self.host)
        self.h.putrequest('GET', self.url)
        self.h.putheader('Range', 'bytes=%d-' % (self.startpos))
        self.h.endheaders()
        #status, reason, headers = h.getreply()
        
        return self.h.getreply()
    
    def getdata(self):
        tname = threading.currentThread().getName()
        newname = self.filename + '.part.'+str(self.fid)
        
        print tname, newname, self.start, self.bsize
        f = open(newname, "wb")
        
        if not self.h:
            self.getheader()
        
        dsize = 0
        while 1:
            data = self.h.file.read(8192)
            if not data:
                return
            dsize = dsize + len(data)
            
            if dsize > self.bsize:
                wlen = len(data) - (dsize - self.bsize)
                f.write(data[:wlen])
                self.counter.set_size(self.fid, wlen)
                break
            elif dsize == self.bsize:
                f.write(data)
                self.counter.set_size(self.fid, dsize)
                break
            else:
                f.write(data)
                self.counter.set_size(self.fid, dsize)   
        print tname, 'read:', dsize     
        f.close()
        
        self.h.close()
    
    
    def run(self):
        self.getheader()
        self.getdata()
    
class Counter:
    def __init__(self):
        self.start = {}
        self.size = {}
    
    def set_both(self, key, val1, val2):
        self.start[key] = val1
        self.size[key] = val2
    
    def set_start(self, key, val):
        self.start[key] = val

    def set_size(self,key, val):
        self.size[key] = val
        
    def get_start(self, key):
        return self.start[key]

    def get_size(self, key):
        return self.size[key]
        
    def __len__(self):
        return len(self.start)

class MainHTTP:
    def __init__(self, url, num=1):
        self.url = url
        self.num = num
        
        urlls = urlparse.urlparse(self.url)
        self.filename = os.path.basename(urlls[2])
        self.host = urlls[1]
    
    def _getdata(self):
        f = open(self.filename+'.part', "wb")
        dsize = 0
        while 1:
            data = self.h.read(8192)
            if not data:
                return
            dsize = dsize + len(data)
            
            if self.size > bsize:
                wlen = len(data) - (self.size-bsize)
                f.write(data[:wlen])                
                break
            elif self.size == bsize:
                f.write(data)                
                break
        f.close()
        os.rename(self.filename+'.part', self.filename)
        print 'ok'
              
    
    def download(self):      
        url = self.url
        self.h = httplib.HTTP()
        #self.h.set_debuglevel(1)
        self.h.connect(self.host)
                
        self.h.putrequest('GET', url)
        self.h.endheaders()
        
        status, reason, headers = self.h.getreply()
           
        
        while status >= 300:            
            if status >= 400:
                print status, reason, headers
                return
            
            try:
                location = headers['Location']
            except Exception, e:
                print e
                return
            last = url
            self.h.putrequest('GET', location)
            self.h.putheader("Referer", last)
            url = 'http://'+self.host + location
            self.h.endheaders()
            
            status, reason, headers = self.h.getreply()
            
        print 'url:', url
        if url != self.url:
            urlls = urlparse.urlparse(self.url)
            self.filename = os.path.basename(urlls[2])
            
        filelen = 0
        try:
            filelen = int(headers['content-length'])
        except Exception, e:
            print e
        # 前面的代码是为了获取到文件大小和真正的文件名
        print 'filelen:', filelen
        
        if filelen == 0:
            self._getdata()
            return
            
        self.h.close()
        counter = Counter()    
        # 计算每个线程下载多大的数据
        bsize = filelen / self.num
        
        # 创建每个线程的计数器
        for i in xrange(1, self.num):
            counter.set_both(i, bsize * (i-1), bsize)
            print 'counter: ', i, bsize * (i-1), bsize
            
        dd = filelen % self.num
        counter.set_both(self.num, bsize*(self.num-1), bsize + dd)
        print 'counter: ', self.num, bsize*(self.num-1), bsize + dd
        
        # 创建线程
        threads = []
        for i in xrange(1, self.num+1):
            print 'create thread:', i
            threads.append(MyHTTP(url, counter, i))
            
        
        for t in threads:
            t.start()
        
        for t in threads:
            t.join()
        
        # 合并临时文件
        print '合并'
        
        # 如果文件已经存在，自动重命名
        i = 0
        filename = self.filename
        while os.path.isfile(filename):
            filename = self.filename+'.'+str(i)
            i = i + 1
            
            
        fw = open(filename, "wb")
        
        for i in xrange(1, self.num+1):
            fname = self.filename+'.part.'+str(i)
            print fname
            if not os.path.isfile(fname):
                print 'not found', fname
                continue
            f1 = open(fname, 'rb')
            while 1:
                data = f1.read(8192)               
                if not len(data):
                    break
                fw.write(data)            
            f1.close()
            os.remove(fname)        
        fw.close()
        print 'all ok'      
        
if __name__ == '__main__':
    url = 'http://downloads.osafoundation.org/PyLucene/src/PyLucene-src-2.2.0-1.tar.gz'
    # url是网址，后面的3是线程数
    h = MainHTTP(url, 3)    
    h.download()

